package com.ibeeking.gmc.global.g.rest.service.jobHandler;

import com.ibeeking.gmc.global.g.rest.service.ITenantDataPushService;
import com.ibeeking.gmc.global.g.rest.service.ITenantService;
import com.ibeeking.gmc.global.g.rest.service.impl.TenantServiceImpl;
import com.ibeeking.found.common.db.annotation.IgnoreDS;
import com.ibeeking.found.common.db.util.DataSourceUtils;
import com.ibeeking.found.common.enums.PublishStatusEnum;
import com.ibeeking.found.common.enums.PushScriptDataEnum;
import com.ibeeking.found.common.constants.JobExecutorHandlerConstant;
import com.ibeeking.gmc.global.service.common.bo.ScriptBO;
import com.ibeeking.gmc.global.service.common.bo.ScriptDatabaseConfigBO;
import com.ibeeking.gmc.global.service.common.vo.TenantVO;

import com.ibeeking.gmc.job.core.biz.model.ReturnT;
import com.ibeeking.gmc.job.core.handler.annotation.XxlJob;
import com.ibeeking.found.job.model.BaseJobModel;
import com.ibeeking.found.job.model.BusinessKeyModel;
import com.ibeeking.found.job.service.IJobService;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.date.DateUtil;
import com.ibeeking.nematos.utils.json.JsonUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName TenantDataPushJobHandler
 * @Description 租户数据分发
 * @Author ibeeking
 * @Date 2021-05-05 17:38
 **/
@Component
public class TenantDataPushJobHandler {

    @Resource
    private ITenantDataPushService tenantDataPushServiceImpl;
    @Resource
    private ITenantService ITenantServiceImpl;
    @Resource
    private IJobService jobServiceImpl;

    @XxlJob(JobExecutorHandlerConstant.TENANT_DATA_PUSH_JOB_HANDLER)
    @IgnoreDS
    public ReturnT<String> tenantDataPushJobHandler(String param) throws Exception {
        Integer tenantId = Integer.valueOf(JsonUtils.fromJson(param, BusinessKeyModel.class).getBusinessKey());
        //判断租户是否有效
        TenantVO view = ITenantServiceImpl.view(tenantId);
        if (null == view || PublishStatusEnum.DISABLE.getValue().equals(view.getPublishStatus()) ||
                !TenantServiceImpl.checkExpire(DateUtil.strToLocalDate(view.getAccountBegin()), DateUtil.strToLocalDate(view.getAccountEnd()))) {
            //无效进行任务删除
            //删除定时任务
            BaseJobModel jobModel = new BaseJobModel();
            jobModel.setBusinessKey(String.valueOf(tenantId));
            jobModel.setExecutorHandler(JobExecutorHandlerConstant.TENANT_DATA_PUSH_JOB_HANDLER);
            jobModel.setType(2);
            jobServiceImpl.delJob(jobModel);
            tenantDataPushServiceImpl.updateScriptStatus(tenantId, null, 0, PushScriptDataEnum.TERMINATION.getCode());
            return ReturnT.SUCCESS;
        }
        //查询执行脚本
        List<ScriptDatabaseConfigBO> scriptDatabaseConfigBos = tenantDataPushServiceImpl.queryExecuteSql(tenantId);
        if (Collections3.isEmpty(scriptDatabaseConfigBos)) {
            return ReturnT.SUCCESS;
        }
        //执行sql
        if (excuteSql(scriptDatabaseConfigBos, tenantId)) {
            return ReturnT.SUCCESS;
        }
        return ReturnT.FAIL;
    }

    private Boolean excuteSql(List<ScriptDatabaseConfigBO> list, Integer tenantId) {
        Map<String, List<ScriptDatabaseConfigBO>> scriptGroup = list.stream().collect(Collectors.groupingBy(ScriptDatabaseConfigBO::getDbName));
        for (Map.Entry<String, List<ScriptDatabaseConfigBO>> entry : scriptGroup.entrySet()) {
            List<ScriptDatabaseConfigBO> values = entry.getValue();
            List<Long> scriptIds = new ArrayList<>();
            List<String> sqls = new ArrayList<>();
            values.stream().forEach(s -> {
                List<ScriptBO> scripts = s.getScripts();
                scriptIds.addAll(scripts.stream().map(ScriptBO::getId).collect(Collectors.toList()));
                sqls.addAll(scripts.stream().map(ScriptBO::getSqlScript).collect(Collectors.toList()));
            });
            //更新执行状态
            tenantDataPushServiceImpl.updateScriptStatus(tenantId, scriptIds, 1, PushScriptDataEnum.EXECUTING.getCode());
            Integer status = PushScriptDataEnum.EXECUTE_FAIL.getCode();
            Connection conn = null;
            try {
                ScriptDatabaseConfigBO config = values.get(0);
                String dbName = entry.getKey().substring(0, entry.getKey().lastIndexOf("_") + 1) + tenantId;
                conn = DataSourceUtils.getConnection(config.getHost(), config.getPort(), dbName, config.getUserName(), config.getPassword());
                if (null == conn) {
                    throw new BusinessException("模板数据源连接失败");
                }
                // 关闭自动提交
                conn.setAutoCommit(false);
                Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
                for (String sql : sqls) {
                    stmt.addBatch(sql);
                }
                //3. 执行数据脚本
                stmt.executeBatch();
                conn.commit();
                conn.close();

                //更新状态
                status = PushScriptDataEnum.EXECUTE_SUCCESS.getCode();
            } catch (Exception ex) {
                status = PushScriptDataEnum.EXECUTE_FAIL.getCode();
                throw new BusinessException("执行脚本异常", ex);
            } finally {
                if (null != conn) {
                    try {
                        conn.close();
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                }
                tenantDataPushServiceImpl.updateScriptStatus(tenantId, scriptIds, 0, status);
            }
        }
        return true;
    }
}
